﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using OpenTK.Graphics;
namespace Foundry.Graphics
{

    public interface IUniform<T>
    {
        int Location { get; }
        string Name { get; }
        bool Active { get; }
        ShaderProgram Program { get; }

        T Value { get; set; }
    }

	/// <summary>
	/// Represents a generic uniform. This class is abstract 
	/// </summary>
    public abstract class Uniform<T> : IUniform<T>
    {
        public int Location { get; private set; }
        public string Name { get; private set; }
        public ShaderProgram Program { get; private set; }
        public bool Active { get { return Location > -1; } }
        private T internal_value;
        internal Uniform(ShaderProgram program, string name)
        {
			Program = program;
            Name = name;
            Location = GL.GetUniformLocation(program.Handle, name);
        }

        public T Value 
		{ 
			get 
			{ 
				return internal_value;
			} 
			set 
			{ 
				internal_value = value;
				// Determine if the parent program is active
				// and make it current if not
				int current_prog = 0;
				GL.GetInteger(GetPName.CurrentProgram, out current_prog);
				if(current_prog != Program.Handle)
					Program.MakeCurrent();
				if(Location > -1)
					SetValue(value);
				Extensions.AssertGL();
			} 
		}

        protected abstract void SetValue(T value);
        
    }
	/// <summary>
	/// Represents a uniform with a scalar float value 
	/// </summary>
    public sealed class ScalarFloatUniform : Uniform<float>
    {

        internal ScalarFloatUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }

        protected override void SetValue(float value)
        {
            GL.Uniform1(Location, value);
        }

    }
	/// <summary>
	/// Represents a uniform with a scalar integer value 
	/// </summary>

    public sealed class ScalarIntUniform : Uniform<int>
    {
        internal ScalarIntUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(int value)
        {
            GL.Uniform1(Location, value);
        }
    }
	
	/// <summary>
	/// Represents a uniform with a 2D vector float value 
	/// </summary>

    public sealed class Vector2Uniform : Uniform<Vector2>
    {
        internal Vector2Uniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Vector2 value)
        {
            GL.Uniform2(Location, ref value);
        }
    }
	/// <summary>
	/// Represents a uniform with a 3D vector float value 
	/// </summary>

    public sealed class Vector3Uniform : Uniform<Vector3>
    {
        internal Vector3Uniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Vector3 value)
        {
            GL.Uniform3(Location, ref value);
        }
    }
	/// <summary>
	/// Represents a uniform with a 4D vector float value 
	/// </summary>

    public sealed class Vector4Uniform : Uniform<Vector4>
    {
        internal Vector4Uniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Vector4 value)
        {
            GL.Uniform4(Location, ref value);
        }
    }
	
	/// <summary>
	/// Represents a uniform with a color value
	/// </summary>
	
    public sealed class Color4Uniform : Uniform<Color4>
    {
        internal Color4Uniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Color4 value)
        {
            GL.Uniform4(Location, value);
        }
    }
	/// <summary>
	/// Represents a uniform with a quaternion value. This is representet by a 4-dimensional vector.
	/// </summary>
	
    public sealed class QuaternionUniform : Uniform<Quaternion>
    {
        internal QuaternionUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Quaternion value)
        {
            GL.Uniform4(Location, value);
        }
    }
	
	/// <summary>
	/// Represents a uniform with a matrix value 
	/// </summary>
	
    public sealed class MatrixUniform : Uniform<Matrix4>
    {
        internal MatrixUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Matrix4 value)
        {
            GL.UniformMatrix4(Location, false, ref value);
        }
    }
	/// <summary>
	/// Represents a uniform with a 1D texture value 
	/// </summary>
	
    public sealed class Texture1DUniform : Uniform<Texture1D>
    {
        internal Texture1DUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Texture1D value)
        {
            GL.Uniform1(Location, value.Handle);
        }
    }
	/// <summary>
	/// Represents a uniform with a 2D texture value 
	/// </summary>

	public sealed class Texture2DUniform : Uniform<Texture2D>
    {
        internal Texture2DUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Texture2D value)
        {
            GL.Uniform1(Location, value.Handle);
        }
    }
	/// <summary>
	/// Represents a uniform with a 3D texture value 
	/// </summary>
	
    public sealed class Texture3DUniform : Uniform<Texture3D>
    {
        internal Texture3DUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(Texture3D value)
        {
            GL.Uniform1(Location, value.Handle);
        }
    }
	/// <summary>
	/// Represents a uniform with a cube texture value 
	/// </summary>
	
    public sealed class TextureCubeUniform : Uniform<CubeTexture>
    {
        internal TextureCubeUniform(ShaderProgram program, string name)
            : base(program, name)
        {
        }
        protected override void SetValue(CubeTexture value)
        {
            GL.Uniform1(Location, value.Handle);
        }
    }

}
